/*
 * 描述：程序主入口
 * 作者：雁风
*/

#include <filesystem>
#include <llvm/CodeGen/CommandFlags.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/InitLLVM.h>
#include <llvm/Support/MemoryBuffer.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/ToolOutputFile.h>
#include <llvm/Support/WithColor.h>
#include <llvm/Support/ConvertUTF.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/MC/TargetRegistry.h>

#include "Compilation.h"

using namespace llvm;

const char *RSC_VERSION = "0.1";

static codegen::RegisterCodeGenFlags CGF;

static cl::list<std::string>
    InputFiles(cl::Positional, cl::desc("<input-files>"));

static cl::opt<std::string>
    MTriple("mtriple",
            cl::desc("Override target triple for module"));

static cl::opt<bool>
    EmitLLVM("emit-llvm-ir",
             cl::desc("Emit IR code instead of assembler"),
             cl::init(false));

static cl::opt<signed char> OptLevel(
    "mode", cl::desc("Setting the optimization mode:"),
    cl::values(
        clEnumValN(0, "debug", "(default) No Optimizations"),
        clEnumValN(1, "release", "Optimize for fast binary"),
        clEnumValN(2, "release-small", "like \'release\', but reduces code size")),
    cl::init(0));

void printVersion(llvm::raw_ostream &OS) {
    OS << "Red/System compiler v" << RSC_VERSION << "\n";
    OS << "  Default target: " << llvm::sys::getDefaultTargetTriple() << "\n";
    std::string CPU(llvm::sys::getHostCPUName());
    OS << "  Host CPU: " << CPU << "\n\n";
    OS.flush();
    llvm::TargetRegistry::printRegisteredTargetsForVersion(OS);
    exit(EXIT_SUCCESS);
}

TargetMachine * createTargetMachine(const char *Argv0) {
    Triple triple(!MTriple.empty() ? Triple::normalize(MTriple) : sys::getDefaultTargetTriple());

    TargetOptions targetOptions = codegen::InitTargetOptionsFromCodeGenFlags(triple);
    std::string CPUStr = codegen::getCPUStr();
    std::string featureStr = codegen::getFeaturesStr();

    std::string err;
    const Target *target = TargetRegistry::lookupTarget(codegen::getMArch(), triple, err);

    if (!target) {
        WithColor::error(llvm::errs(), Argv0) << err;
        return nullptr;
    }

    llvm::TargetMachine *TM = target->createTargetMachine(
        triple.getTriple(), CPUStr, featureStr, targetOptions,
        llvm::Optional<Reloc::Model>(Reloc::Model::PIC_));
    return TM;
}

int main(int Argc, const char **Argv) {
    llvm::InitLLVM X(Argc, Argv);

    InitializeNativeTarget();
    InitializeNativeTargetAsmPrinter();
    InitializeNativeTargetAsmParser();

    llvm::cl::SetVersionPrinter(&printVersion);
    llvm::cl::ParseCommandLineOptions(Argc, Argv);

    if (Argc == 1) {
        llvm::WithColor::error(llvm::errs(), Argv[0])
            << "no input files" << "\n";
        exit(EXIT_FAILURE);
    }

    llvm::TargetMachine *TM = createTargetMachine(Argv[0]);
    if (!TM) exit(EXIT_FAILURE);

    // Do compilation
    for (int i = 0; i < InputFiles.size(); i++) {
        Compilation comp(InputFiles[i], TM);
        comp.run();
    }

    delete TM;
    return 0;
}